package com.ham.mud.characters;

import com.ham.mud.Colors;
import com.ham.mud.LookAtAble;
import com.ham.mud.ServerConnection;
import com.ham.mud.classes.MudClass;
import com.ham.mud.commands.skills.Skill;
import com.ham.mud.commands.spells.Spell;
import com.ham.mud.items.EquipSlot;
import com.ham.mud.items.Item;
import com.ham.mud.items.Weapon;
import com.ham.mud.races.Race;

import java.util.*;

/**
 * Created by hlucas on Jun 21, 2011 at 12:55:15 PM
 */
public abstract class MudCharacter implements LookAtAble {
    public static final Random rand = new Random();
    
    protected List<Item> inventory;
    protected Map<EquipSlot, Item> equipment;
    protected Vitals vitals;
    protected MudClass mudClass;
    protected Race race;
    protected Stats stats;
    protected Experience experience;

    protected MudCharacter() {
        inventory = new ArrayList<Item>();
        equipment = new HashMap<EquipSlot, Item>();
    }

    public List<Item> getInventory() {
        return inventory;
    }

    public void setInventory(List<Item> inventory) {
        this.inventory = inventory;
    }

    public void addItem(Item item) {
        inventory.add(item);
    }

    public void removeItem(Item item) {
        inventory.remove(item);
    }

    public Map<EquipSlot, Item> getEquipment() {
        return equipment;
    }

    public void setEquipment(Map<EquipSlot, Item> equipment) {
        this.equipment = equipment;
    }

    public List<Spell> getSpells() {
        return mudClass.getSpells(experience);
    }

    public List<Skill> getSkills() {
        return mudClass.getSkills(experience);
    }

    public Vitals getVitals() {
        return vitals;
    }

    public void decrementHealth(int by) {
        vitals.decrementHealth(by);
    }

    public MudClass getMudClass() {
        return mudClass;
    }

    public void setMudClass(MudClass mudClass) {
        this.mudClass = mudClass;
        this.setStats(mudClass.getBaseStats());
    }

    public int getLevel() {
        return mudClass.getLevel(experience);
    }

    public int getToNextLevel() {
        return mudClass.getToNextLevel(experience);
    }

    public Race getRace() {
        return race;
    }

    public void setRace(Race race) {
        this.race = race;
        this.setStats(race.getStats());
    }

    public Stats getStats() {
        return stats;
    }

    public void setStats(Stats stats) {
        if(this.stats == null) {
            this.stats = stats;
        } else {
            this.stats.add(stats);
        }
    }

    public void setExperience(Experience experience) {
        this.experience = experience;
        this.vitals = mudClass.getVitals(experience);
        applyStatsToVitals();
    }

    public Experience getExperience() {
        return experience;
    }

    public int getExperienceGain() {
        return experience.getCount() / 5;
    }

    public void addExperience(int amount, ServerConnection connection) {
        connection.println("You gain " + amount + " experience points." + Colors.NEW_LINE);
        int tnl = getToNextLevel();
        experience.add(amount);
        if(tnl <= amount) {
            level(connection);
        }
    }
    
    public void level(ServerConnection connection) {
        connection.println("You have gained a level!" + Colors.NEW_LINE);
        this.vitals = mudClass.getVitals(experience);
        applyStatsToVitals();
    }

    private void applyStatsToVitals() {
        this.vitals.setMaxHealth((int) (((double)this.vitals.getMaxHealth())*getConstitutionModifier()));
        this.vitals.setMaxMove((int) (((double)this.vitals.getMaxMove())*getDexterityModifier()));
        this.vitals.setMaxMana((int) (((double)this.vitals.getMaxMana())*getIntelligenceModifier()));
        this.vitals.setHealth(this.vitals.getMaxHealth());
        this.vitals.setMove(this.vitals.getMaxMove());
        this.vitals.setMana(this.vitals.getMaxMana());
    }

    public void maxVitals() {
        vitals.setHealth(vitals.getMaxHealth());
        vitals.setMana(vitals.getMaxMana());
        vitals.setMove(vitals.getMaxMove());
    }

    public int getCombatDamageRoll() {
        int roll = rand.nextInt(getLevel()*2 + 1);
        Weapon weapon = (Weapon) getEquipment().get(EquipSlot.WIELDED_PRIMARY);
        if(weapon != null) {
            roll += weapon.getDiceRoll();
        }
        
        roll = (int) Math.round(getStrengthModifier() * (double) roll);
        if(getMissRoll()) {
            roll = 0;
        }

        return roll;
    }

    private boolean getMissRoll() {
        return rand.nextInt(getStats().getDexterity() + 1) == 0;
    }

    public double getStrengthModifier() {
        double strength = (double) getStats().getStrength();
        return strength / 18.0;
    }

    public double getIntelligenceModifier() {
        double intelligence = (double) getStats().getIntelligence();
        return intelligence / 18.0;
    }

    public double getDexterityModifier() {
        double dexterity = (double) getStats().getDexterity();
        return dexterity / 18.0;
    }

    public double getConstitutionModifier() {
        double constitution = (double) getStats().getConstitution();
        return constitution / 18.0;
    }

    public void equip(Item item, ServerConnection connection) {
        Item currentlyEquipped = equipment.get(item.getWornAt());
        if(currentlyEquipped != null) {
            unequip(currentlyEquipped, connection);
        }
        removeItem(item);
        equipment.put(item.getWornAt(), item);
        if(currentlyEquipped != null) {
            connection.printSolo(Colors.NEW_LINE + "You equip " + item.getName() + ".");
        } else {
            connection.printSolo("You equip " + item.getName() + ".");
        }
    }

    public void unequip(Item item, ServerConnection connection) {
        equipment.put(item.getWornAt(), null);
        addItem(item);
        connection.printSolo("You unequip " + item.getName() + ".");
    }

    public void display(ServerConnection connection) {
        connection.printStart(getLongDescription());
        showEquipment(connection);
        connection.printEnd();
    }

    public void showEquipment(ServerConnection connection) {
        Set<EquipSlot> slots = equipment.keySet();

        for(EquipSlot slot : slots) {
            Item item = equipment.get(slot);
            connection.println("    <" + slot.getName() + "> " + Colors.getSpaces(15, slot.getName()) + item.getName() + item.getAffects());
        }
    }

    public void showItems(ServerConnection connection) {
        List<Item> items = getInventory();

        for(Item item : items) {
            connection.println(item.getName());
        }
    }
}
